
// import haxe.ds.Vector;
// import haxe.macro.Expr;
// import oimo.common.Mat4;
// import oimo.common.Vec3;
// import oimo.m.M;
// import oimo.common.Quat;

import { Vec3 } from "./Vec3";
import { Mat3 } from "./Mat3";
import { Mat4 } from "./Mat4";
import { Quat } from "./Quat";

/**
 * The object pool system of `Vec3`, `Mat3`, `Mat4`, and `Quat`.
 */
export class Pool {
	stackVec3: Array<Vec3>;
	stackMat3: Array<Mat3>;
	stackMat4: Array<Mat4>;
	stackQuat: Array<Quat>;

	/**
	 * Default constructor.
	 */
	constructor() {
		this.stackVec3 = new Array<Vec3>(256);
		this.stackMat3 = new Array<Mat3>(256);
		this.stackMat4 = new Array<Mat4>(256);
		this.stackQuat = new Array<Quat>(256);
	}

	/**
	 * Returns a `Vec3` object. If an unused object of `Vec3` is pooled, this does
	 * not create a new instance.
	 */
	public vec3(): Vec3 {
		let result = this.stackVec3.pop();
		return result ? result : new Vec3();
	}

	/**
	 * Returns a `Mat3` object. If an unused object of `Mat3` is pooled, this does
	 * not create a new instance.
	 */
	public mat3(): Mat3 {
		let result = this.stackMat3.pop();
		return result ? result : new Mat3();
	}

	/**
	 * Returns a `Mat4` object. If an unused object of `Vec3` is pooled, this does
	 * not create a new instance.
	 */
	public mat4(): Mat4 {
		let result = this.stackMat4.pop();
		return result ? result : new Mat4();
	}

	/**
	 * Returns a `Quat` object. If an unused object of `Quat` is pooled, this does
	 * not create a new instance.
	 */
	public quat(): Quat {
		let result = this.stackQuat.pop();
		return result ? result : new Quat();
	}

	/**
	 * Disposes an object got from `Pool.vec3`, `Pool.mat3`, `Pool.mat4`, or `Pool.quat`.
	 */
	public dispose(vec3?: Vec3, mat3?: Mat3, mat4?: Mat4, quat?: Quat): void {
		if (vec3 != null) {
			this.disposeVec3(vec3);
		}
		if (mat3 != null) {
			this.disposeMat3(mat3);
		}
		if (mat4 != null) {
			this.disposeMat4(mat4);
		}
		if (quat != null) {
			this.disposeQuat(quat);
		}
	}

	/**
	 * Disposes an `Vec3` object got from `Pool.vec3`.
	 */
	public disposeVec3(v: Vec3): void {
		v.zero();
		this._dispose(this.stackVec3, v);
	}

	/**
	 * Disposes an `Mat3` object got from `Pool.mat3`.
	 */
	public disposeMat3(m: Mat3): void {
		m.identity();
		this._dispose(this.stackMat3, m);
	}

	/**
	 * Disposes an `Mat4` object got from `Pool.mat4`.
	 */
	public disposeMat4(m: Mat4): void {
		m.identity();
		this._dispose(this.stackMat4, m);
	}

	/**
	 * Disposes an `Quat` object got from `Pool.quat`.
	 */
	public disposeQuat(q: Quat): void {
		q.identity();
		this._dispose(this.stackQuat, q);
	}

	_dispose<T>(stack: Array<T>, obj: T): T {
		stack.push(obj);
		return obj;
	}

}
